#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/i2c.h"

// LCD参数定义
#define LCD_ADDR              0x27    // LCD I2C地址，可能需要根据实际模块调整
#define LCD_COLS              20      // 20列显示
#define I2C_MASTER_PORT_NUM   0       // I2C端口号

// 控制位定义
#define LCD_RS_BIT            0x01    // 寄存器选择位
#define LCD_RW_BIT            0x02    // 读/写选择位
#define LCD_EN_BIT            0x04    // 使能位
#define LCD_BACKLIGHT         0x08    // 背光控制位

// LCD命令定义
#define LCD_CLEARDISPLAY      0x01
#define LCD_RETURNHOME        0x02
#define LCD_ENTRYMODESET      0x04
#define LCD_DISPLAYCONTROL    0x08
#define LCD_CURSORSHIFT       0x10
#define LCD_FUNCTIONSET       0x20
#define LCD_SETCGRAMADDR      0x40
#define LCD_SETDDRAMADDR      0x80

// 显示控制标志
#define LCD_DISPLAYON         0x04
#define LCD_DISPLAYOFF        0x00
#define LCD_CURSORON          0x02
#define LCD_CURSOROFF         0x00
#define LCD_BLINKON           0x01
#define LCD_BLINKOFF          0x00

// 显示模式设置
#define LCD_ENTRYRIGHT        0x00
#define LCD_ENTRYLEFT         0x02
#define LCD_ENTRYSHIFTINCREMENT 0x01
#define LCD_ENTRYSHIFTDECREMENT 0x00

// LCD设备结构体
typedef struct {
    uint8_t rows;                    // 显示行数
    uint8_t *row_offsets;            // 行偏移地址数组
    uint8_t addr;                    // I2C地址
    uint8_t backlight_state;         // 背光状态
} lcd_t;

// 全局LCD实例
static lcd_t lcd_device;

// 发送4位数据到LCD
static void lcd_send_nibble(uint8_t nibble, uint8_t flags) {
    uint8_t data = (nibble << 4) | flags | lcd_device.backlight_state;

    i2c_cmd_handle_t cmd = i2c_cmd_link_create();
    i2c_master_start(cmd);
    i2c_master_write_byte(cmd, (lcd_device.addr << 1) | I2C_MASTER_WRITE, true);

    // 发送数据，使能位设为0
    i2c_master_write_byte(cmd, data & ~LCD_EN_BIT, true);

    // 发送数据，使能位设为1（产生上升沿）
    i2c_master_write_byte(cmd, data | LCD_EN_BIT, true);

    // 发送数据，使能位设为0（产生下降沿）
    i2c_master_write_byte(cmd, data & ~LCD_EN_BIT, true);

    i2c_master_stop(cmd);
    i2c_master_cmd_begin(I2C_MASTER_PORT_NUM, cmd, 1000 / portTICK_RATE_MS);
    i2c_cmd_link_delete(cmd);

    vTaskDelay(1 / portTICK_RATE_MS);
}

// 发送命令到LCD
void lcd_send_cmd(uint8_t cmd) {
    lcd_send_nibble(cmd >> 4, 0);        // 发送高4位
    lcd_send_nibble(cmd & 0x0F, 0);      // 发送低4位
    vTaskDelay(2 / portTICK_RATE_MS);    // 大多数命令需要约1ms
}

// 发送数据到LCD
void lcd_send_data(uint8_t data) {
    lcd_send_nibble(data >> 4, LCD_RS_BIT);  // 发送高4位
    lcd_send_nibble(data & 0x0F, LCD_RS_BIT);  // 发送低4位
    vTaskDelay(1 / portTICK_RATE_MS);    // 数据传输延时
}

// 根据行数设置行偏移地址
static void lcd_set_row_offsets(uint8_t rows) {
    // 释放之前的行偏移数组
    if (lcd_device.row_offsets) {
        free(lcd_device.row_offsets);
    }

    // 分配新的行偏移数组
    lcd_device.row_offsets = (uint8_t*)malloc(rows * sizeof(uint8_t));
    if (!lcd_device.row_offsets) {
        ESP_LOGE("LCD", "Failed to allocate memory for row offsets");
        return;
    }

    // 根据行数设置行偏移
    switch (rows) {
        case 1:
            lcd_device.row_offsets[0] = 0x00;
            break;
        case 2:
            lcd_device.row_offsets[0] = 0x00;
            lcd_device.row_offsets[1] = 0x40;
            break;
        case 3:
            lcd_device.row_offsets[0] = 0x00;
            lcd_device.row_offsets[1] = 0x40;
            lcd_device.row_offsets[2] = 0x14;
            break;
        case 4:
        default:
            lcd_device.row_offsets[0] = 0x00;
            lcd_device.row_offsets[1] = 0x40;
            lcd_device.row_offsets[2] = 0x14;
            lcd_device.row_offsets[3] = 0x54;
            break;
    }

    lcd_device.rows = rows;
}

// 初始化LCD，接收行数作为参数
esp_err_t lcd_init(uint8_t rows, uint8_t addr) {
    // 验证行数参数
    if (rows < 1 || rows > 4) {
        ESP_LOGE("LCD", "Invalid number of rows: %d (must be 1-4)", rows);
        return ESP_ERR_INVALID_ARG;
    }

    // 初始化LCD设备结构体
    lcd_device.addr = addr;
    lcd_device.backlight_state = LCD_BACKLIGHT;  // 默认打开背光

    // 设置行偏移
    lcd_set_row_offsets(rows);

    // 等待LCD上电稳定
    vTaskDelay(50 / portTICK_RATE_MS);

    // 初始化为8位模式（4位模式初始化序列的一部分）
    lcd_send_nibble(0x03, 0);
    vTaskDelay(5 / portTICK_RATE_MS);

    // 重复一次
    lcd_send_nibble(0x03, 0);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 再重复一次，然后切换到4位模式
    lcd_send_nibble(0x03, 0);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 设置为4位模式
    lcd_send_nibble(0x02, 0);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 根据行数设置显示参数
    uint8_t num_lines = (rows > 1) ? 0x08 : 0x00;  // 0x08表示2行或更多

    // 设置显示参数：4位模式，行数，5x8点阵
    lcd_send_cmd(LCD_FUNCTIONSET | num_lines | 0x04);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 关闭显示
    lcd_send_cmd(LCD_DISPLAYCONTROL | LCD_DISPLAYOFF);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 清屏
    lcd_send_cmd(LCD_CLEARDISPLAY);
    vTaskDelay(5 / portTICK_RATE_MS);  // 清屏需要较长时间

    // 设置输入模式：左对齐，不移屏
    lcd_send_cmd(LCD_ENTRYMODESET | LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 打开显示，无光标，不闪烁
    lcd_send_cmd(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
    vTaskDelay(1 / portTICK_RATE_MS);

    ESP_LOGI("LCD", "Initialized LCD with %d rows at address 0x%02X", rows, addr);
    return ESP_OK;
}

// 发送字符串到LCD
void lcd_send_string(const char *str) {
    while (*str) {
        lcd_send_data(*str++);
    }
}

// 设置光标位置
void lcd_set_cursor(uint8_t col, uint8_t row) {
    if (row >= lcd_device.rows) row = lcd_device.rows - 1;
    if (col >= LCD_COLS) col = LCD_COLS - 1;

    uint8_t addr = LCD_SETDDRAMADDR | (col + lcd_device.row_offsets[row]);
    lcd_send_cmd(addr);
}

// 清屏
void lcd_clear(void) {
    lcd_send_cmd(LCD_CLEARDISPLAY);
    vTaskDelay(5 / portTICK_RATE_MS);
}

// 打开背光
void lcd_backlight_on(void) {
    lcd_device.backlight_state = LCD_BACKLIGHT;
    // 发送一个空命令以更新背光状态
    lcd_send_cmd(0x00);
}

// 关闭背光
void lcd_backlight_off(void) {
    lcd_device.backlight_state = 0;
    // 发送一个空命令以更新背光状态
    lcd_send_cmd(0x00);
}

// 更改LCD行数
esp_err_t lcd_change_rows(uint8_t rows) {
    if (rows < 1 || rows > 4) {
        ESP_LOGE("LCD", "Invalid number of rows: %d (must be 1-4)", rows);
        return ESP_ERR_INVALID_ARG;
    }

    // 保存当前显示内容
//    uint8_t display_state = 0;
    lcd_send_cmd(LCD_DISPLAYCONTROL);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 设置新的行偏移
    lcd_set_row_offsets(rows);

    // 根据新行数设置显示参数
    uint8_t num_lines = (rows > 1) ? 0x08 : 0x00;
    lcd_send_cmd(LCD_FUNCTIONSET | num_lines | 0x04);
    vTaskDelay(1 / portTICK_RATE_MS);

    // 恢复显示状态
    lcd_send_cmd(LCD_DISPLAYCONTROL | LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF);
    vTaskDelay(1 / portTICK_RATE_MS);

    ESP_LOGI("LCD", "Changed LCD to %d rows", rows);
    return ESP_OK;
}

// 获取当前行数
uint8_t lcd_get_rows(void) {
    return lcd_device.rows;
}

// 释放LCD资源
void lcd_deinit(void) {
    if (lcd_device.row_offsets) {
        free(lcd_device.row_offsets);
        lcd_device.row_offsets = NULL;
    }
    ESP_LOGI("LCD", "LCD resources deinitialized");
}
